package com.hoppinzq.aop;

import com.alibaba.druid.stat.JdbcStatManager;
import com.alibaba.fastjson.JSONObject;
import com.hoppinzq.service.auth.AuthenticationContext;
import com.hoppinzq.service.common.UserPrincipal;
import com.hoppinzq.service.log.bean.LogContext;
import com.hoppinzq.service.log.bean.ServiceLogBean;
import com.hoppinzq.service.trace.TraceContext;
import com.hoppinzq.service.trace.bean.TraceBean;
import com.hoppinzq.service.util.DateFormatUtil;
import com.hoppinzq.service.util.UUIDUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Aspect
@Component
public class TraceIdAspect {

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    private long start = 0L;
    private String traceId = null;

    @Pointcut("@annotation(com.hoppinzq.aop.TraceDuridSQL)")
    public void duridMethods() {
    }

    // 在方法执行之前获取traceId
    @Before("duridMethods()")
    public void before(JoinPoint joinPoint) {
        traceId = request.getHeader("traceId");
        if(TraceContext.getPrincipal()==null){
            start=System.currentTimeMillis();
            if(traceId!=null){
                String id= UUIDUtil.getUUID();
                TraceContext.enter();
                TraceBean traceBean=new TraceBean(traceId);
                traceBean.setId(id);
                List<TraceBean> traceBeanList=new ArrayList<>();
                traceBeanList.add(traceBean);
                TraceContext.setPrincipal(traceBeanList);
            }
        }else{
            List<TraceBean> traceBeanList=TraceContext.getPrincipal();
            start=System.currentTimeMillis();
            TraceBean traceBean=new TraceBean(traceId);
            String id= UUIDUtil.getUUID();
            traceBean.setId(id);
            traceBeanList.add(traceBean);
            TraceContext.setPrincipal(traceBeanList);
        }
    }

    @AfterThrowing(pointcut = "duridMethods()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        if (traceId != null) {
            List<TraceBean> traceBeans = TraceContext.getPrincipal();
            TraceBean traceBean = traceBeans.get(traceBeans.size() - 1);
            try {
                traceBean.setException(e.getMessage());
            }catch (Exception ex){}finally {
                traceBean.setClazz(joinPoint.getSignature().getDeclaringTypeName());
                UserPrincipal upp=(UserPrincipal) AuthenticationContext.getPrincipal();
                traceBean.setUserPrincipal(upp);
                ServiceLogBean serviceLog=(ServiceLogBean) LogContext.getPrincipal();
                traceBean.setServiceLogBean(serviceLog);
                long end=System.currentTimeMillis();
                traceBean.setCreateTime(end);
                traceBean.setCostTime(end-start);
                response.addHeader("x-rpc-trace", JSONObject.toJSONString(traceBeans));
                TraceContext.exit();
            }
        }

    }

    // 在方法执行之后设置log到响应头
    @AfterReturning(value = "duridMethods()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        if(traceId!=null){
            List<TraceBean> traceBeans=TraceContext.getPrincipal();
            TraceBean traceBean=traceBeans.get(traceBeans.size()-1);
            List<Map> sqlMaps=new ArrayList<>();
            try{
                JdbcStatManager jdbcStatManager = JdbcStatManager.getInstance();
                TabularData sqlList = jdbcStatManager.getSqlList();
                Collection<?> rows = sqlList.values();
                for (Object row : rows) {
                    Map sqlMap=new HashMap();
                    CompositeData compositeData = (CompositeData) row;
                    sqlMap.put("id",compositeData.get("ID"));
                    sqlMap.put("sql",compositeData.get("SQL"));
                    sqlMap.put("dBType",compositeData.get("DbType"));
                    sqlMap.put("successCount",compositeData.get("EffectedRowCount"));
                    sqlMap.put("errorCount",compositeData.get("ErrorCount"));
                    sqlMap.put("time",compositeData.get("TotalTime"));
                    sqlMap.put("url",compositeData.get("URL"));
                    Object error=compositeData.get("LastErrorMessage");
                    sqlMap.put("error",error);
                    if(error==null){
                        sqlMap.put("isSuccess",true);
                    }else{
                        sqlMap.put("isSuccess",false);
                    }
                    sqlMaps.add(sqlMap);
                }
                traceBean.setSqlMap(sqlMaps);
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                traceBean.setClazz(joinPoint.getSignature().getDeclaringTypeName());
                UserPrincipal upp=(UserPrincipal) AuthenticationContext.getPrincipal();
                traceBean.setUserPrincipal(upp);
                ServiceLogBean serviceLog=(ServiceLogBean) LogContext.getPrincipal();
                traceBean.setServiceLogBean(serviceLog);
                long end=System.currentTimeMillis();
                traceBean.setCreateTime(end);
                traceBean.setCostTime(end-start);
                response.addHeader("x-rpc-trace", JSONObject.toJSONString(traceBeans));
                TraceContext.exit();
            }
        }
    }
}